/*
 * This file is part of Viper.  Viper is free software: you can
 * redistribute it and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation, version 2.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Copyright GruntXProductions
 */

using System;
using System.Runtime.InteropServices;
using System.IO;

namespace Viper
{
	public unsafe partial class GEX
	{
		
		public static uint SymIndex = 0;
		public static Assembly Open(string file, bool DS = true)
		{
			BinaryReader br = new BinaryReader(new FileStream(file,FileMode.Open));
			Assembly asm = new Assembly();
			byte[] buff = br.ReadBytes((int)br.BaseStream.Length);
			br.Close();
			fixed(byte* ptr = buff)
			{
				GEXHdr* header = (GEXHdr*)ptr;
				for(uint i = 0; i < header->sectionCount;i++)
				{
					GEXSHdr* section = ((GEXSHdr*)(ptr + (i * sizeof(GEXSHdr)) + header->sectionTable));
					if(getString(section->name).Substring(0,5) == ".text")
					{
						byte* text = ((byte*)ptr + section->offset);
						uint ssize = section->size;
						byte[] code = new byte[section->size];
						for(uint p = 0; p < ssize;p++)
							code[p] = text[p];
						for(int b = 0; b < code.Length;)
						{
							byte size = code[b + 1];
							byte[] data = new byte[size + 2];
							for(int d = 0; d < (size + 2) && b < code.Length;d++)
								data[d] = text[b + d];
							Instruction ins = new Instruction();
							ins.Bytecode = data;
							b += size + 2;
							ins.Address = (uint)b;
							asm.innerCode.Add(ins);
						}
					}
					else if(getString(section->name).Substring(0,5) == ".data")
					{
						byte*  data = ((byte*)ptr + section->offset);
						asm.data = new byte[section->size];
						for(uint p = 0; p < section->size;p++)
						{
							asm.data[p] = data[p];
					
						}
					}
					else if(getString(section->name).Substring(0,5) == ".meta")
					{
						byte*  data = ((byte*)ptr + section->offset);
						string accum = "";
						for(uint p = 0; p < section->size;p++)
						{
							accum += ((char)data[p]).ToString();
					
						}
						foreach(string ln in accum.Split('\n'))
						{
							string line = ln.Trim();
							if(line.Contains("="))
							{
								string name = line.Substring(0, line.IndexOf('=')).Trim();
								string val = line.Substring(line.IndexOf('=') + 1).Trim();
								asm.Metadata.Add(name, val);
							}
						}
					}
					else if(getString(section->name).Substring(0,6) == ".reloc")
					{
						byte* reloc = ((byte*)ptr + section->offset);
						for(int p = 0; p < section->size;p += 9)
						{
							uint address = BitConverter.ToUInt32(new byte[]{reloc[p + 0],reloc[p + 1],reloc[p + 2],reloc[p + 3]},0);
							uint symbol = BitConverter.ToUInt32(new byte[]{reloc[p + 4],reloc[p + 5],reloc[p + 6],reloc[p + 7]},0);
							byte type = reloc[p + 8];
							Relocation recl = new Relocation();
							recl.Address = address;
							recl.Index = symbol;
							recl.RelocationType = type;
							asm.RelocData.Add(recl);
							
						}
					}
					else if (getString(section->name).Substring(0,7) == ".symtab")
					{
						byte* symtab = ((byte*)ptr + section->offset);
						int pos = 0;
						for(int s = 0; s < section->size;s++)
						{
							Symbol sym = new Symbol();
							string name = getString(((byte*)symtab + pos)).Trim();
							pos += name.Length + 1;
							uint address = BitConverter.ToUInt32(new byte[]{symtab[pos],symtab[pos + 1],symtab[pos + 2],symtab[pos + 3]},0);
							pos += 4;
							if(name != "")
							{
								sym.Name = name;
								sym.Global = true;
							}
							else
								sym.Name = "symbol_" + SymIndex.ToString("X8");
							SymIndex++;
							sym.Address = address;
							
							asm.SymbolTable.Add(sym);
						}
					}
				}
			}
			if(DS)
				return Disassembler.Disassemble(asm);
			else
				return asm;
		}
	}
}

